// AntonyDlg.cpp : implementation file
//

#include "stdafx.h"
#include <stdarg.h>
#include "Antony.h"
#include "AntonyDlg.h"
#include "Utils.h"
#include "TaxiAgent.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

CAntonyDlg::CAntonyDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CAntonyDlg::IDD, pParent)
{
    srand(time(NULL));
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    m_env = NULL;
    m_maxCars = 250;
}

void CAntonyDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_TABS, m_dlgTabCtrl);
    DDX_Control(pDX, IDC_BTN_ADDTAXI, m_dlgAddTaxiBtn);
    DDX_Control(pDX, IDC_BTN_RUNSIM, m_dlgRunSimBtn);
    DDX_Control(pDX, IDC_BTN_PAUSESIM, m_dlgPauseSimBtn);
    DDX_Control(pDX, IDC_BTN_RESET, m_dlgResetSimBtn);
    DDX_Control(pDX, IDC_ENV, m_dlgMapImage);
    DDX_Control(pDX, IDC_CHECK1, m_dlgShowAllNodes);
    DDX_Control(pDX, IDC_CHECK2, m_dlgShowConn);
    DDX_Control(pDX, IDC_GRP_MAP, m_dlgMapGrp);
    DDX_Control(pDX, IDC_EDT_CARCOUNT, m_dlgCarCount);
    DDX_Control(pDX, IDC_CHECK4, m_dlgShowFares);
    DDX_Control(pDX, IDC_CHECK5, m_dlgShowCars);
    DDX_Control(pDX, IDC_TIMESLIDER, m_dlgSpeedSlide);
    DDX_Control(pDX, IDC_EDIT3, m_dlgSpeedEdit);
}

BEGIN_MESSAGE_MAP(CAntonyDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
    ON_NOTIFY(TCN_SELCHANGE, IDC_TABS, &CAntonyDlg::OnTabSelChange)
    ON_BN_CLICKED(IDC_CHECK1, &CAntonyDlg::OnClicked_ShowAllNodes)
    ON_BN_CLICKED(IDC_CHECK2, &CAntonyDlg::OnClicked_ShowConns)
    ON_WM_DESTROY()
    ON_WM_TIMER()
    ON_BN_CLICKED(IDC_BTN_RUNSIM, &CAntonyDlg::OnClicked_Play)
    ON_BN_CLICKED(IDC_BTN_PAUSESIM, &CAntonyDlg::OnClicked_Pause)
    ON_BN_CLICKED(IDC_BTN_ADDTAXI, &CAntonyDlg::OnClick_AddAgent)
    ON_BN_CLICKED(IDC_BUTTON1, &CAntonyDlg::OnClicked_Reset)
    ON_EN_CHANGE(IDC_EDT_CARCOUNT, &CAntonyDlg::OnChange_CarCount)
    ON_BN_CLICKED(IDC_CHECK4, &CAntonyDlg::OnClicked_ShowFares)
    ON_BN_CLICKED(IDC_CHECK5, &CAntonyDlg::OnClicked_ShowCars)
    ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_TIMESLIDER, &CAntonyDlg::OnReleased_Slider)
END_MESSAGE_MAP()


// CAntonyDlg message handlers

BOOL CAntonyDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// Open environment data
    CFileDialog openDlg(TRUE, ".txt", "", 0, "Text Files (*.txt)|*.txt||"); 
    if (openDlg.DoModal() == IDOK)
    {
        m_env = new CEnv(openDlg.GetPathName());
        m_dlgMapGrp.SetWindowText(m_env->GetMapDesc());
    }

    if (!m_env)
    {
        AfxMessageBox("Failed to create environment!");
        return FALSE;
    }

    m_dlgMapImage.SetNodeMap(m_env->GetNodeMap());
    m_dlgMapImage.SetAgentVec(m_env->GetAgents());
    m_dlgShowConn.EnableWindow(FALSE);
    m_dlgPauseSimBtn.EnableWindow(FALSE);

    CString str;
    str.Format("%d", m_maxCars);
    m_dlgCarCount.SetWindowText(str);

    str.Format("%d", m_env->GetAgentSpeed());
    m_dlgSpeedEdit.SetWindowText(str);

    m_dlgSpeedSlide.SetRange(SPEED_MIN, SPEED_MAX);
    m_dlgSpeedSlide.SetPos(m_env->GetAgentSpeed());

    CAgentUtils::Seed();

    // Set default display properties 
    m_dlgShowFares.SetCheck(BST_CHECKED);       
    OnClicked_ShowFares();
    m_dlgMapImage.m_drawCars = false;
    m_dlgMapImage.m_drawTaxis = true;

    InitTabs();
    InitLists();

    CString msg;
    msg.Format("%d nodes (intersections) processed.", m_env->GetNodeMap()->m_vecNodes.size());
    Log(msg);
    Log("Initialization complete");

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CAntonyDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

HCURSOR CAntonyDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CAntonyDlg::InitTabs()
{
    m_dlgTabCtrl.InsertItem(0, "Log");
    m_dlgTabCtrl.InsertItem(1, "Agents");
    m_dlgTabCtrl.InsertItem(2, "Environment");
}

void CAntonyDlg::InitLists()
{
    DWORD dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | WS_BORDER 
        | LVS_NOSORTHEADER | LVS_SINGLESEL;
    int titleHeight = ::GetSystemMetrics(SM_CYCAPTION);
    int borderWidth = ::GetSystemMetrics(SM_CXEDGE);

    // Get size
    CRect rect;
    GetDlgItem(IDC_LIST_PLC)->GetWindowRect(&rect);
    rect.MoveToXY(rect.TopLeft().x - borderWidth, rect.TopLeft().y - titleHeight);

    // Log
    m_dlgLogList.Create(dwStyle, rect, this, WM_USER + 1);
    m_dlgLogList.InsertColumn(0, "Timestamp");
    m_dlgLogList.InsertColumn(1, "Message");
    m_dlgLogList.ShowWindow(SW_SHOW);
    AutoSizeList(m_dlgLogList);

    // Agents
    m_dlgAgentList.Create(dwStyle, rect, this, WM_USER + 2);
    m_dlgAgentList.InsertColumn(0, "ID");
    m_dlgAgentList.InsertColumn(1, "Age");
    m_dlgAgentList.InsertColumn(2, "Status");
    m_dlgAgentList.InsertColumn(3, "Has Job");
    m_dlgAgentList.InsertColumn(4, "On Job");
    m_dlgAgentList.InsertColumn(5, "Fares Done");
    m_dlgAgentList.InsertColumn(6, "Cash");
    m_dlgAgentList.InsertColumn(7, "Income per min");
    AutoSizeList(m_dlgAgentList);
    m_dlgAgentList.SetExtendedStyle(m_dlgAgentList.GetExtendedStyle() | LVS_EX_FULLROWSELECT);

    // Environment
    m_dlgEnvList.Create(dwStyle, rect, this, WM_USER + 3);
    m_dlgEnvList.InsertColumn(0, "ID");
    m_dlgEnvList.InsertColumn(1, "Type");
    m_dlgEnvList.InsertColumn(2, "Status");
    AutoSizeList(m_dlgEnvList);
}

void CAntonyDlg::OnTabSelChange(NMHDR *pNMHDR, LRESULT *pResult)
{
    *pResult = 0;

    m_dlgLogList.ShowWindow(SW_HIDE);
    m_dlgAgentList.ShowWindow(SW_HIDE);
    m_dlgEnvList.ShowWindow(SW_HIDE);

    switch (m_dlgTabCtrl.GetCurSel())
    {
        case 0: m_dlgLogList.ShowWindow(SW_SHOW); break;
        case 1: m_dlgAgentList.ShowWindow(SW_SHOW); break;
        case 2: m_dlgEnvList.ShowWindow(SW_SHOW); break;
        default: AfxMessageBox("Unknown tab selected"); break;
    }
}


void CAntonyDlg::AutoSizeList(CListCtrl& list)
{
    for (int i = 0; i < list.GetHeaderCtrl()->GetItemCount(); i++)
    {
        list.SetColumnWidth(i, 100);
    }
}

void CAntonyDlg::Log(const CString& msg)
{
    int pos = m_dlgLogList.GetItemCount();
    CTime time = CTime::GetCurrentTime();    
    m_dlgLogList.InsertItem(pos, time.Format("%H:%M:%S"));
    m_dlgLogList.SetItem(pos, 1, LVIF_TEXT, msg, 0, 0, 0, 0);
}

void CAntonyDlg::OnClicked_ShowAllNodes()
{
    m_env->GetNodeMap()->m_drawNodes = m_dlgShowAllNodes.GetCheck() == BST_CHECKED;
    m_dlgShowConn.EnableWindow(m_dlgShowAllNodes.GetCheck());
    //m_dlgMapImage.Invalidate();
    Invalidate();
}

void CAntonyDlg::OnClicked_ShowConns()
{
    m_env->GetNodeMap()->m_drawConnections = m_dlgShowConn.GetCheck() == BST_CHECKED;
    m_dlgMapImage.Invalidate();
}

void CAntonyDlg::OnClicked_ShowFares()
{
    m_env->GetNodeMap()->m_drawFares = m_dlgShowFares.GetCheck() == BST_CHECKED;
    m_dlgMapImage.Invalidate();
}

void CAntonyDlg::OnClicked_ShowCars()
{
    m_dlgMapImage.m_drawCars = m_dlgShowCars.GetCheck() == BST_CHECKED;
}


void CAntonyDlg::OnDestroy()
{
    CDialog::OnDestroy();

    if (m_env)
        delete m_env;
}

void CAntonyDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: Add your message handler code here and/or call default
    if (m_env)
    {
        while (m_env->GetNumCars() < m_maxCars)
        {
            m_env->SpawnCarAgent();
        }

        m_env->Step(TIMER_RATE);        

        m_dlgMapImage.Invalidate();

        if (m_nextUpdate <= CSysTime::Now())
        {
            UpdateInfo();
            m_nextUpdate = CSysTime::Now() + CSysTime::Span(0, 500);
        }
    }
    else
    {
        KillTimer(TIMER_ID);
        TRACE("NULL environment pointer\n");
        ASSERT(FALSE);
    }

    CDialog::OnTimer(nIDEvent);
}

void CAntonyDlg::OnClicked_Play()
{    
    m_dlgRunSimBtn.EnableWindow(FALSE);
    m_dlgPauseSimBtn.EnableWindow(TRUE);
    SetTimer(TIMER_ID, TIMER_RATE, NULL);
}

void CAntonyDlg::OnClicked_Pause()
{
    m_dlgRunSimBtn.EnableWindow(TRUE);
    m_dlgPauseSimBtn.EnableWindow(FALSE);
    KillTimer(TIMER_ID);
}

void CAntonyDlg::OnClick_AddAgent()
{
    m_env->SpawnTaxiAgent();
    UpdateInfo();
}

void CAntonyDlg::UpdateInfo()
{
    // TODO: Keep a true log of all the environment details within the CEnv class.
    //       Update m_dlgLogList with any new entries into that log.
    // UpdateLog();

    UpdateAgents();

    // UpdateEnv();

    m_dlgMapImage.Invalidate();
}

void CAntonyDlg::UpdateAgents()
{
    m_dlgAgentList.SetRedraw(FALSE);

    // Empty the list
    m_dlgAgentList.DeleteAllItems();

    // Add all the data
    for (CAgentPtrVecIter it = m_env->GetAgents()->begin();
         it != m_env->GetAgents()->end(); ++it)
    {
        if ((*it)->GetType() != ETYPE_TAXI) continue;
        CTaxiAgent* agent = (CTaxiAgent*)(*it);

        int pos = m_dlgAgentList.GetItemCount();

        CString msg;
        msg.Format("%d", agent->GetId());
        m_dlgAgentList.InsertItem(pos, msg, NULL);
        msg.Format("%.2f", CSysTime::Now().GetSeconds() - agent->m_birth.GetSeconds());
        m_dlgAgentList.SetItem(pos, 1, LVIF_TEXT, msg, NULL, NULL, NULL, NULL);
        m_dlgAgentList.SetItem(pos, 2, LVIF_TEXT, agent->GetStatusString(), NULL, NULL, NULL, NULL);
        msg.Format("%s", agent->HasJob() ? "Yes" : "No");
        m_dlgAgentList.SetItem(pos, 3, LVIF_TEXT, msg, NULL, NULL, NULL, NULL);
        msg.Format("%s", agent->OnJob() ? "Yes" : "No");
        m_dlgAgentList.SetItem(pos, 4, LVIF_TEXT, msg, NULL, NULL, NULL, NULL);
        msg.Format("%d", agent->m_faresCompleted);
        m_dlgAgentList.SetItem(pos, 5, LVIF_TEXT, msg, NULL, NULL, NULL, NULL);
        msg.Format("%.2f", agent->m_cash);
        m_dlgAgentList.SetItem(pos, 6, LVIF_TEXT, msg, NULL, NULL, NULL, NULL);
        msg.Format("%.2f", agent->m_profitRate);
        m_dlgAgentList.SetItem(pos, 7, LVIF_TEXT, msg, NULL, NULL, NULL, NULL);
    }

    for (int i = 0; i < 8; i++)
        m_dlgAgentList.SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);

    m_dlgAgentList.SetRedraw(TRUE);

}
void CAntonyDlg::OnClicked_Reset()
{
    if (m_env)
        delete m_env;

    m_env = new CEnv(MAPFILE);
    UpdateInfo();
}

void CAntonyDlg::OnChange_CarCount()
{
    CString str;
    m_dlgCarCount.GetWindowText(str);
    m_maxCars = atoi(str);
}

void CAntonyDlg::OnReleased_Slider(NMHDR *pNMHDR, LRESULT *pResult)
{
    int pos = m_dlgSpeedSlide.GetPos();

    m_env->SetAgentSpeed(pos);
    
    CString str;
    str.Format("%d", pos);
    m_dlgSpeedEdit.SetWindowText(str);

    *pResult = 0;
}
